/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

#include <syscall.h>
#include <syscalls.h>

#include <stdio.h>
#include <string.h>
#include <time.h>

/* Macro trick to generate svc instructions with given number */
#define SVC_INSTRUCTION(number) "svc #" #number "\n"

#define SYSTEM_CALL(number, args_var, ret_var)  \
  __asm__ volatile (                            \
    "mov r1, %[args]\n"                         \
    SVC_INSTRUCTION(number)                     \
    "mov %[ret], r0\n"                          \
    : [ret] "=r" (ret_var)                      \
    : [args] "r" (args_var)                     \
    : "r1", "r0"                                \
  );                                            \

void sleep(int seconds)
{
  struct sys_sleep sleep_args;
  int ret;

  sleep_args.seconds = seconds;

  SYSTEM_CALL(SYS_sleep, &sleep_args, ret);
}

int opendir(const char name[256])
{
  struct sys_opendir opendir_args;
  int ret = 0;

  strlcpy(opendir_args.name, name, 256);

  SYSTEM_CALL(SYS_opendir, &opendir_args, ret);

  return ret;
}

static struct dir_entry tmp_dir_entry;

struct dir_entry *readdir(int handle)
{
  struct sys_readdir readdir_args;
  int ret = 0;

  readdir_args.handle = handle;

  SYSTEM_CALL(SYS_readdir, &readdir_args, ret);

  if (ret != 0)
    return NULL;

  strlcpy(tmp_dir_entry.name, readdir_args.name, 256);
  tmp_dir_entry.type = readdir_args.type;
  tmp_dir_entry.size = readdir_args.size;

  return &tmp_dir_entry;
}

void closedir(int handle)
{
  struct sys_closedir closedir_args;
  int ret = 0;

  closedir_args.handle = handle;

  SYSTEM_CALL(SYS_closedir, &closedir_args, ret);
}

void
exit(int exit_code)
{
  struct sys_exit exit_args;
  int ret = 0;

  exit_args.exit_code = exit_code;

  SYSTEM_CALL(SYS_exit, &exit_args, ret);
}

int
open(const char name[256])
{
  struct sys_open open_args;
  int ret = 0;

  strlcpy(open_args.name, name, 256);

  SYSTEM_CALL(SYS_open, &open_args, ret);

  return ret;
}

int
read(int handle, void *buff, size_t nbytes)
{
  struct sys_read read_args;
  int ret = 0;

  read_args.handle = handle;
  read_args.nbytes = nbytes;

  SYSTEM_CALL(SYS_read, &read_args, ret);

  if (ret > 0)
    memcpy(buff, read_args.buff, nbytes);

  return ret;
}

int
write(int handle, const void *buff, size_t nbytes)
{
  struct sys_write write_args;
  int ret = 0;

  write_args.handle = handle;
  write_args.nbytes = nbytes;
  memcpy(write_args.buff, buff, nbytes);

  SYSTEM_CALL(SYS_write, &write_args, ret);

  return ret;
}

void
close(int handle)
{
  struct sys_close close_args;
  int ret = 0;

  close_args.handle = handle;

  SYSTEM_CALL(SYS_close, &close_args, ret);
}

int
seek(int handle, off_t offset, int whence)
{
  struct sys_seek seek_args;
  int ret = 0;

  seek_args.handle = handle;
  seek_args.offset = offset;
  seek_args.whence = whence;

  SYSTEM_CALL(SYS_seek, &seek_args, ret);

  return ret;
}

int
ioctl(int handle, uint32_t request, uint32_t param)
{
  struct sys_ioctl ioctl_args;
  int ret = 0;

  ioctl_args.handle = handle;
  ioctl_args.request = request;
  ioctl_args.param = param;

  SYSTEM_CALL(SYS_ioctl, &ioctl_args, ret);

  return ret;
}

int
fork(void)
{
  int ret = 0;

  SYSTEM_CALL(SYS_fork, NULL, ret);

  return ret;
}

int
execve(const char *filename, const char *argv)
{
  int ret = 0;
  struct sys_execve execve_args;

  strlcpy(execve_args.filename, filename, 256);
  strlcpy(execve_args.argv, argv, 256);

  SYSTEM_CALL(SYS_execve, &execve_args, ret);

  return ret;
}

int
stat(const char *filename, int *type, int *size)
{
  int ret = 0;
  struct sys_stat stat_args;

  strlcpy(stat_args.name, filename, 256);

  SYSTEM_CALL(SYS_stat, &stat_args, ret);

  if (ret >= 0)
    {
      if (type)
        *type = stat_args.type;
      if (size)
        *size = stat_args.size;
    }

  return ret;
}

int
wait(int *exit_code)
{
  int ret = 0;
  struct sys_wait wait_args;

  SYSTEM_CALL(SYS_wait, &wait_args, ret);

  if (exit_code)
    *exit_code = wait_args.exit_code;

  return ret;
}

int
chdir(const char *directory)
{
  int ret = 0;
  struct sys_chdir chdir_args;

  strlcpy(chdir_args.directory, directory, 256);

  SYSTEM_CALL(SYS_chdir, &chdir_args, ret);

  return ret;
}

void
getwd(char *buffer, size_t size)
{
  int ret = 0;
  struct sys_getwd getwd_args;

  SYSTEM_CALL(SYS_getwd, &getwd_args, ret);

  strlcpy(buffer, getwd_args.directory, size);
}

int
mount(const char *fs, const char *mountpoint)
{
  int ret = 0;
  struct sys_mount mount_args;

  strlcpy(mount_args.fs, fs, 32);
  strlcpy(mount_args.mountpoint, mountpoint, 256);

  SYSTEM_CALL(SYS_mount, &mount_args, ret);

  return ret;
}

void
get_processes(struct proc_info *processes, int *n_processes)
{
  int ret = 0;
  struct sys_get_processes get_proc;

  SYSTEM_CALL(SYS_get_processes, &get_proc, ret);

  if (processes)
    {
      int i;

      for (i = 0; i < get_proc.n_processes; i++)
        {
          processes[i].pid = get_proc.processes[i].pid;
          processes[i].cpu_time = get_proc.processes[i].cpu_time;
        }
    }

  if (n_processes)
    *n_processes = get_proc.n_processes;
}

void*
mmap(void *addr, size_t len, int prot, int flags, int filedes, off_t offset)
{
  int ret = 0;
  struct sys_mmap mmap_args;

  mmap_args.retval = NULL;
  mmap_args.addr = addr;
  mmap_args.len = len;
  mmap_args.prot = prot;
  mmap_args.flags = flags;
  mmap_args.fd = filedes;
  mmap_args.offset = offset;

  SYSTEM_CALL(SYS_mmap, &mmap_args, ret);

  return mmap_args.retval;
}


// Munmap ignores the len param for now
int
munmap(void *addr, size_t len)
{
  int ret = 0;
  volatile struct sys_munmap munmap_args;

  munmap_args.addr = addr;
  munmap_args.len = len;

  SYSTEM_CALL(SYS_munmap, &munmap_args, ret);

  return ret;
}

int
clock_gettime(clockid_t clk_id, struct timespec *tp)
{
  int ret = 0;
  struct sys_clock_gettime gettime_args;

  gettime_args.clk_id = clk_id;
  gettime_args.tp = tp;

  SYSTEM_CALL(SYS_clock_gettime, &gettime_args, ret);

  return ret;
}

int
clock_settime(clockid_t clk_id, const struct timespec *tp)
{
  int ret = 0;
  struct sys_clock_settime settime_args;

  settime_args.clk_id = clk_id;
  settime_args.tp = tp;

  SYSTEM_CALL(SYS_clock_settime, &settime_args, ret);

  return ret;
}

int
setprocparam(proc_param_t param, int64_t val)
{
  int ret = 0;
  struct sys_setprocparam setprocparam_args;

  setprocparam_args.param = param;
  setprocparam_args.value = val;
  SYSTEM_CALL(SYS_setprocparam, &setprocparam_args, ret);

  return ret;
}

int64_t
getprocparam(proc_param_t param)
{
  int ret = 0;
  struct sys_getprocparam getprocparam_args;

  getprocparam_args.param = param;
  SYSTEM_CALL(SYS_getprocparam, &getprocparam_args, ret);

  return getprocparam_args.result;
}

int
mkdir(const char path[256])
{
  struct sys_mkdir mkdir_args;
  int ret = 0;

  strlcpy(mkdir_args.name, path, 256);

  SYSTEM_CALL(SYS_mkdir, &mkdir_args, ret);

  return ret;
}

int
rmdir(const char path[256])
{
  struct sys_rmdir rmdir_args;
  int ret = 0;

  strlcpy(rmdir_args.name, path, 256);

  SYSTEM_CALL(SYS_rmdir, &rmdir_args, ret);

  return ret;
}
